// $Id: response.c 38 2011-04-15 14:37:02Z cedric.shih@gmail.com $
/*
 * Copyright (C) 2010 Cedric Shih <cedric.shih@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdlib.h>
#include <string.h>

#include <nio.h>
#include <nio/buffer.h>

#include "headers.h"
#include "response.h"

#include "niohttp/test.h"

#define BUFFER_SIZE 1024

static struct niohttp_response *resp;
static struct niobuffer *buffer;

static void test_setup()
{
	resp = niohttp_response_new();
	buffer = niobuffer_new(BUFFER_SIZE);
}

static void test_teardown()
{
	niobuffer_free(buffer);
	niohttp_response_free(resp);
}

static void test_init()
{
	TEST_ASSERT_NOT_NULL(buffer);
	TEST_ASSERT_NOT_NULL(resp);
}

static char headerless[] = {
		//0    1    2    3    4    5    6    7    8    9
		 'H', 'T', 'T', 'P', '/', '1', '.', '1', ' ', '4', // 0
		 '2', '6', ' ', 'U', 'p', 'g', 'r', 'a', 'd', 'e', // 1
		 ' ', 'R', 'e', 'q', 'u', 'i', 'r', 'e', 'd','\r', // 2
		'\n','\r','\n'
};

static void test_headerless()
{
	int rc;
	size_t pos;
	size_t i, len = sizeof(headerless) / sizeof(headerless[0]);

	niohttp_response_reset(resp);
	niohttp_headers_clear(resp->header);
	niobuffer_clear(buffer);

	rc = niobuffer_puts(buffer, (unsigned char *)headerless, len);
	TEST_ASSERT_EQUAL_INT(0, rc);
	pos = niobuffer_position(buffer);

	niobuffer_flip(buffer);

	rc = niohttp_response_parse(resp, buffer);
	TEST_ASSERT_EQUAL_INT(0, rc);
	TEST_ASSERT_EQUAL_INT(pos, niobuffer_position(buffer));

	TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_METADATA, resp->state);
	TEST_ASSERT_NOT_NULL(resp->protocol);
	TEST_ASSERT_EQUAL_STRING("HTTP", resp->protocol);
	TEST_ASSERT_NOT_NULL(resp->version.string);
	TEST_ASSERT_EQUAL_STRING("1.1", resp->version.string);
	TEST_ASSERT_EQUAL_INT(426, resp->code);
	TEST_ASSERT_NOT_NULL(resp->reason);
	TEST_ASSERT_EQUAL_STRING("Upgrade Required", resp->reason);

	TEST_ASSERT_EQUAL_INT(0, niohttp_headers_size(resp->header));

	niohttp_response_reset(resp);
	niohttp_headers_clear(resp->header);
	niobuffer_clear(buffer);

	for (i = 0; i < len; i++) {
		rc = niobuffer_put(buffer, headerless[i]);
		TEST_ASSERT_EQUAL_INT(0, rc);
		pos = niobuffer_position(buffer);

		niobuffer_flip(buffer);

		rc = niohttp_response_parse(resp, buffer);
		TEST_ASSERT_EQUAL_INT(0, rc);
		TEST_ASSERT_EQUAL_INT(pos, niobuffer_position(buffer));
		rc = niobuffer_set_limit(buffer, niobuffer_capacity(buffer));

		if (i < 4) {
			TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_LINE, resp->state);
			TEST_ASSERT_NULL(resp->protocol);
			TEST_ASSERT_NULL(resp->version.string);
			TEST_ASSERT_EQUAL_INT(0, resp->code);
			TEST_ASSERT_NULL(resp->reason);
			TEST_ASSERT_EQUAL_INT(0, niohttp_headers_size(resp->header));
		} else if (i < 8) {
			TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_LINE, resp->state);
			TEST_ASSERT_NOT_NULL(resp->protocol);
			TEST_ASSERT_EQUAL_STRING("HTTP", resp->protocol);
			TEST_ASSERT_NULL(resp->version.string);
			TEST_ASSERT_EQUAL_INT(0, resp->code);
			TEST_ASSERT_NULL(resp->reason);
			TEST_ASSERT_EQUAL_INT(0, niohttp_headers_size(resp->header));
		} else if (i < 12) {
			TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_LINE, resp->state);
			TEST_ASSERT_NOT_NULL(resp->protocol);
			TEST_ASSERT_EQUAL_STRING("HTTP", resp->protocol);
			TEST_ASSERT_NOT_NULL(resp->version.string);
			TEST_ASSERT_EQUAL_STRING("1.1", resp->version.string);
			TEST_ASSERT_EQUAL_INT(0, resp->code);
			TEST_ASSERT_NULL(resp->reason);
			TEST_ASSERT_EQUAL_INT(0, niohttp_headers_size(resp->header));
		} else if (i < 29) {
			TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_LINE, resp->state);
			TEST_ASSERT_NOT_NULL(resp->protocol);
			TEST_ASSERT_EQUAL_STRING("HTTP", resp->protocol);
			TEST_ASSERT_NOT_NULL(resp->version.string);
			TEST_ASSERT_EQUAL_STRING("1.1", resp->version.string);
			TEST_ASSERT_EQUAL_INT(426, resp->code);
			TEST_ASSERT_NULL(resp->reason);
			TEST_ASSERT_EQUAL_INT(0, niohttp_headers_size(resp->header));
		} else if (i == 30) {
			TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_HEADER_NAME, resp->state);
			TEST_ASSERT_NOT_NULL(resp->protocol);
			TEST_ASSERT_EQUAL_STRING("HTTP", resp->protocol);
			TEST_ASSERT_NOT_NULL(resp->version.string);
			TEST_ASSERT_EQUAL_STRING("1.1", resp->version.string);
			TEST_ASSERT_EQUAL_INT(426, resp->code);
			TEST_ASSERT_NOT_NULL(resp->reason);
			TEST_ASSERT_EQUAL_STRING("Upgrade Required", resp->reason);
			TEST_ASSERT_EQUAL_INT(0, niohttp_headers_size(resp->header));
		}
	}

	TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_METADATA, resp->state);
	TEST_ASSERT_EQUAL_INT(0, niohttp_headers_size(resp->header));
}

static char headered[] = {
		//0    1    2    3    4    5    6    7    8    9
		 'H', 'T', 'T', 'P', '/', '1', '.', '1', ' ', '4', // 0
		 '2', '6', ' ', 'U', 'p', 'g', 'r', 'a', 'd', 'e', // 1
		 ' ', 'R', 'e', 'q', 'u', 'i', 'r', 'e', 'd','\r', // 2
		 '\n','U', 'p', 'g', 'r', 'a', 'd', 'e', ':', ' ', // 3
		 'T', 'L', 'S', '/', '1', '.', '0', ',', ' ', 'H', // 4
		 'T', 'T', 'P', '/', '1', '.', '1','\r','\n', 'C', // 5
		 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', ':', // 6
		 ' ', 'U', 'p', 'g', 'r', 'a', 'd', 'e','\r','\n', // 7
		'\r','\n'
};

static void test_headered()
{
	int rc;
	size_t pos;
	size_t i, len = sizeof(headered) / sizeof(headered[0]);

	niohttp_response_reset(resp);
	niohttp_headers_clear(resp->header);
	niobuffer_clear(buffer);

	rc = niobuffer_puts(buffer, (unsigned char *)headered, len);
	TEST_ASSERT_EQUAL_INT(0, rc);
	pos = niobuffer_position(buffer);

	niobuffer_flip(buffer);

	rc = niohttp_response_parse(resp, buffer);
	TEST_ASSERT_EQUAL_INT(0, rc);
	TEST_ASSERT_EQUAL_INT(pos, niobuffer_position(buffer));

	TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_METADATA, resp->state);
	TEST_ASSERT_NOT_NULL(resp->protocol);
	TEST_ASSERT_EQUAL_STRING("HTTP", resp->protocol);
	TEST_ASSERT_NOT_NULL(resp->version.string);
	TEST_ASSERT_EQUAL_STRING("1.1", resp->version.string);
	TEST_ASSERT_EQUAL_INT(426, resp->code);
	TEST_ASSERT_NOT_NULL(resp->reason);
	TEST_ASSERT_EQUAL_STRING("Upgrade Required", resp->reason);

	TEST_ASSERT(0 != niohttp_headers_size(resp->header));
	TEST_ASSERT_EQUAL_STRING("TLS/1.0, HTTP/1.1",
			niohttp_headers_get(resp->header, "Upgrade"));
	TEST_ASSERT_EQUAL_STRING("Upgrade",
			niohttp_headers_get(resp->header, "Connection"));

	niohttp_response_reset(resp);
	niohttp_headers_clear(resp->header);
	niobuffer_clear(buffer);

	for (i = 0; i < len; i++) {
		rc = niobuffer_put(buffer, headered[i]);
		TEST_ASSERT_EQUAL_INT(0, rc);
		pos = niobuffer_position(buffer);

		niobuffer_flip(buffer);

		rc = niohttp_response_parse(resp, buffer);
		TEST_ASSERT_EQUAL_INT(0, rc);
		TEST_ASSERT_EQUAL_INT(pos, niobuffer_position(buffer));
		rc = niobuffer_set_limit(buffer, niobuffer_capacity(buffer));

		if (i < 4) {
			TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_LINE, resp->state);
			TEST_ASSERT_NULL(resp->protocol);
			TEST_ASSERT_NULL(resp->version.string);
			TEST_ASSERT_EQUAL_INT(0, resp->code);
			TEST_ASSERT_NULL(resp->reason);
			TEST_ASSERT_EQUAL_INT(0, niohttp_headers_size(resp->header));
		} else if (i < 8) {
			TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_LINE, resp->state);
			TEST_ASSERT_NOT_NULL(resp->protocol);
			TEST_ASSERT_EQUAL_STRING("HTTP", resp->protocol);
			TEST_ASSERT_NULL(resp->version.string);
			TEST_ASSERT_EQUAL_INT(0, resp->code);
			TEST_ASSERT_NULL(resp->reason);
			TEST_ASSERT_EQUAL_INT(0, niohttp_headers_size(resp->header));
		} else if (i < 12) {
			TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_LINE, resp->state);
			TEST_ASSERT_NOT_NULL(resp->protocol);
			TEST_ASSERT_EQUAL_STRING("HTTP", resp->protocol);
			TEST_ASSERT_NOT_NULL(resp->version.string);
			TEST_ASSERT_EQUAL_STRING("1.1", resp->version.string);
			TEST_ASSERT_EQUAL_INT(0, resp->code);
			TEST_ASSERT_NULL(resp->reason);
			TEST_ASSERT_EQUAL_INT(0, niohttp_headers_size(resp->header));
		} else if (i < 29) {
			TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_LINE, resp->state);
			TEST_ASSERT_NOT_NULL(resp->protocol);
			TEST_ASSERT_EQUAL_STRING("HTTP", resp->protocol);
			TEST_ASSERT_NOT_NULL(resp->version.string);
			TEST_ASSERT_EQUAL_STRING("1.1", resp->version.string);
			TEST_ASSERT_EQUAL_INT(426, resp->code);
			TEST_ASSERT_NULL(resp->reason);
			TEST_ASSERT_EQUAL_INT(0, niohttp_headers_size(resp->header));
		} else if (i == 30) {
			TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_HEADER_NAME, resp->state);
			TEST_ASSERT_NOT_NULL(resp->protocol);
			TEST_ASSERT_EQUAL_STRING("HTTP", resp->protocol);
			TEST_ASSERT_NOT_NULL(resp->version.string);
			TEST_ASSERT_EQUAL_STRING("1.1", resp->version.string);
			TEST_ASSERT_EQUAL_INT(426, resp->code);
			TEST_ASSERT_NOT_NULL(resp->reason);
			TEST_ASSERT_EQUAL_STRING("Upgrade Required", resp->reason);
			TEST_ASSERT_EQUAL_INT(0, niohttp_headers_size(resp->header));
		}
	}

	TEST_ASSERT_EQUAL_INT(NIOHTTP_RESP_METADATA, resp->state);
	TEST_ASSERT(0 != niohttp_headers_size(resp->header));
	TEST_ASSERT_EQUAL_STRING("TLS/1.0, HTTP/1.1",
			niohttp_headers_get(resp->header, "Upgrade"));
	TEST_ASSERT_EQUAL_STRING("Upgrade",
			niohttp_headers_get(resp->header, "Connection"));
}

TestRef niohttp_response_test()
{
	EMB_UNIT_TESTFIXTURES(fixtures) {
		new_TestFixture("niohttp_response_test_init", test_init),
		new_TestFixture("niohttp_response_test_headered",
				test_headered),
		new_TestFixture("niohttp_response_test_headerless",
				test_headerless),
	};
	EMB_UNIT_TESTCALLER(test_case, "niohttp_response_test", test_setup, test_teardown, fixtures);
	return (TestRef)&test_case;
}
